/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.java;
import java.io.*;
import java.util.Iterator;
import java.util.LinkedList;
import javax.swing.text.StyledDocument;
import javax.swing.text.Position;
import org.openide.cookies.ConnectionCookie;
import org.openide.src.*;
import org.openide.text.*;
import org.openide.util.RequestProcessor;
/** Element that describes one class.
*
* @author Petr Hamernik
*/
final class ClassElementImpl extends MemberElementImpl implements ClassElement.Impl {
/** is class or interface */
boolean isClass;
/** property, need not be initialized */
Identifier superclass;
/** Interfaces */
Identifier[] interfaces;
/** collection of initializers */
ElementsCollection.Initializer initializers;
/** collection of constructors */
ElementsCollection.Constructor constructors;
/** collection of methods */
ElementsCollection.Method methods;
/** collection of fields */
ElementsCollection.Field fields;
/** collection of classes */
ElementsCollection.Class classes;
/** This variable holds the reference back to SourceElementImpl.DataRef,
* which is there refernced only using a SoftReference. So, when exists this
* class it is impossible to garbage collected the data holding
* information about the source.
*/
Object hook;
static final long serialVersionUID =-8212915557419039595L;
/** Constructor for the parser.
*/
public ClassElementImpl() {
}
/** Copy constructor.
* @param el element to copy from
*/
public ClassElementImpl(ClassElement el, PositionBounds bounds) throws SourceException {
super(el, bounds);
isClass = el.isClassOrInterface();
superclass = el.getSuperclass();
interfaces = el.getInterfaces();
// javadoc = new JavaDocImpl.Class(el.getJavaDoc().getRawText(), this);
copyFrom(el);
if (bounds != null)
regenerate(el);
}
void updateImpl(ParsingResult.Class c, LinkedList changes, int changesMask) {
updateImpl(c.impl, changes, changesMask);
initCollections(c);
if (initializers != null)
initializers.updateContent(c.initializers, changes, changesMask);
if (fields != null)
fields.updateContent(c.fields, changes, changesMask);
if (constructors != null)
constructors.updateContent(c.constructors, changes, changesMask);
if (methods != null)
methods.updateContent(c.methods, changes, changesMask);
if (classes != null)
classes.updateContent(c.classes, changes, changesMask);
}
/** Redefine to supress firing of PROP_BODY after change of the class' text
*/
protected Object getBodyHash() {
return null;
}
private void initCollections(ParsingResult.Class c) {
if (c.initializers.size() > 0)
initInitializers();
if (c.fields.size() > 0)
initFields();
if (c.constructors.size() > 0)
initConstructors();
if (c.methods.size() > 0)
initMethods();
if (c.classes.size() > 0)
initClasses();
}
void initSubElements(ParsingResult.Class c) {
ClassElement thisClassElement = (ClassElement) element;
initCollections(c);
Iterator it;
it = c.initializers.iterator();
while (it.hasNext())
initializers.add(new InitializerElement((InitializerElementImpl) it.next(), thisClassElement));
it = c.fields.iterator();
while (it.hasNext())
fields.add(new FieldElement((FieldElementImpl) it.next(), thisClassElement));
it = c.constructors.iterator();
while (it.hasNext())
constructors.add(new ConstructorElement((ConstructorElementImpl) it.next(), thisClassElement));
it = c.methods.iterator();
while (it.hasNext())
methods.add(new MethodElement((MethodElementImpl) it.next(), thisClassElement));
it = c.classes.iterator();
while (it.hasNext()) {
ParsingResult.Class addingClass = (ParsingResult.Class) it.next();
classes.add(new ClassElement(addingClass.impl, thisClassElement));
addingClass.impl.initSubElements(addingClass);
}
}
private void updateImpl(ClassElementImpl impl, LinkedList changes, int changesMask) {
boolean changesMatch = ((changesMask & JavaConnections.TYPE_CLASSES_CHANGE) != 0);
MemberElement prevElement = super.updateImpl(impl, changesMatch);
if (isClass != impl.isClass) {
if (changesMatch && (prevElement == null))
prevElement = (ClassElement)(((ClassElement)element).clone());
isClass = impl.isClass;
firePropertyChange(PROP_CLASS_OR_INTERFACE, new Boolean(!isClass), new Boolean(isClass));
}
if ((superclass != impl.superclass) ||
((superclass != null) && !superclass.compareTo(impl.superclass, true))) {
if (changesMatch && (prevElement == null))
prevElement = (ClassElement)(((ClassElement)element).clone());
Identifier old = superclass;
superclass = impl.superclass;
firePropertyChange(PROP_SUPERCLASS, old, superclass);
}
// Interfaces
boolean changed = (impl.interfaces.length != interfaces.length);
if (!changed) {
for (int i = 0; i < interfaces.length; i++) {
if (!interfaces[i].compareTo(impl.interfaces[i], true)) {
changed = true;
break;
}
}
}
if (changed) {
if (changesMatch && (prevElement != null))
prevElement = (ClassElement)(((ClassElement)element).clone());
final Identifier[] old = interfaces;
interfaces = impl.interfaces;
firePropertyChange(PROP_INTERFACES, old, interfaces);
// should be moved to InterfaceConnection
if (isClass) {
// System.out.println ("isClass and has "+old.length+" => "+interfaces.length+" interfaces"); // NOI18N
final boolean[] oldMatches = new boolean[old.length];
final boolean[] newMatches = new boolean[interfaces.length];
for (int i = 0; i < old.length; i++) {
// System.out.println("compare:"+old[i]); // NOI18N
for (int j = 0; j < interfaces.length; j++) {
// System.out.println(" with:"+interfaces[j]); // NOI18N
if (newMatches[j])
continue;
if (old[i].compareTo(interfaces[j], false)) {
// System.out.println(" found."); // NOI18N
oldMatches[i] = true;
newMatches[j] = true;
break;
}
}
}
SourceElementImpl.PARSING_RP.post(new Runnable() {
public void run() {
for (int i = 0; i < old.length; i++) {
if (!oldMatches[i]) {
// System.out.println("unregister:"+old[i]); // NOI18N
findSourceElementImpl().unregisterForName(old[i], JavaConnections.IMPLEMENTS);
}
}
LinkedList addedInterfaces = new LinkedList();
for (int i = 0; i < interfaces.length; i++) {
if (!newMatches[i]) {
// System.out.println("register:"+interfaces[i]); // NOI18N
findSourceElementImpl().registerForName(interfaces[i], JavaConnections.IMPLEMENTS);
addedInterfaces.add(interfaces[i]);
}
}
InterfaceConnection.interfacesAdded(
(Identifier[])addedInterfaces.toArray(new Identifier[0]),
(ClassElement) element,
findSourceElementImpl()
);
}
});
}
}
if (changesMatch && (prevElement != null)) {
changes.add(new JavaConnections.Change(JavaConnections.TYPE_CLASSES_CHANGE, prevElement, element));
}
}
void checkInterfaces() {
for (int i = 0; i < interfaces.length; i++) {
// System.out.println("register:"+interfaces[i]); // NOI18N
findSourceElementImpl().registerForName(interfaces[i], JavaConnections.IMPLEMENTS);
}
}
PositionBounds createBoundsFor(ElementsCollection col) throws SourceException {
ElementsCollection[] cols = {
initializers, fields, constructors, methods, classes
};
boolean after = false;
for (int i = 0; i < cols.length; i++) {
if (after && (cols[i] != null)) {
Object o = cols[i].getFirst();
if (o != null) {
ElementImpl impl = (ElementImpl)((Element) o).getCookie(ElementImpl.class);
PositionRef pos = findSourceElementImpl().findUnguarded(impl.bounds.getBegin(),
this.bodyBounds);
return SourceElementImpl.createBoundsAt(pos);
}
continue;
}
if (col == cols[i]) {
after = true;
}
}
PositionRef where = bounds.getBegin().getEditorSupport().createPositionRef(
bodyBounds.getEnd().getOffset(),
Position.Bias.Forward);
return SourceElementImpl.createBoundsAt(where);
}
/** Late initialization of initialization of copy elements.
*/
public void copyFrom(ClassElement copyFrom) throws SourceException {
changeInitializers(copyFrom.getInitializers(), SET, true);
changeConstructors(copyFrom.getConstructors(), SET, true);
changeMethods(copyFrom.getMethods(), SET, true);
changeFields(copyFrom.getFields(), SET, true);
changeClasses(copyFrom.getClasses(), SET, true);
}
/** Getter for the associated class
* @return the class element for this impl
*/
final ClassElement getClassElement() {
return (ClassElement)element;
}
/** Find the element at the specified offset in the document.
* @param offset The position of the element
* @return the element at the position.
*/
public Element findElement(int offset) {
for (int i = 0; i <= 4; i++) {
ElementsCollection col = null;
switch (i) {
case 0: col = classes; break;
case 1: col = methods; break;
case 2: col = fields; break;
case 3: col = constructors; break;
case 4: col = initializers; break;
}
if (col != null) {
Element retElement = col.findElement(offset);
if (retElement != null)
return retElement;
}
}
return element;
}
/** Setter
*/
public void setSuperclass(Identifier superclass) throws SourceException {
Identifier old = this.superclass;
this.superclass = superclass;
try {
regenerateHeader();
firePropertyChange(PROP_SUPERCLASS, old, superclass);
}
catch (SourceException e) {
this.superclass = old;
throw e;
}
}
/** @return the superclass or empty if this element represents interface. */
public Identifier getSuperclass() {
return superclass;
}
public void setClassOrInterface(boolean isClass) throws SourceException {
boolean old = this.isClass;
this.isClass = isClass;
try {
regenerateHeader();
firePropertyChange(PROP_CLASS_OR_INTERFACE, new Boolean(old), new Boolean(isClass));
}
catch (SourceException e) {
this.isClass = old;
throw e;
}
}
/** @return true if this element represents the class otherwise
* for the interfaces returns false.
*/
public boolean isClassOrInterface() {
return isClass;
}
// ========================= Interfaces ==================================
/** Changes interfaces this class implements(or extends).
* @param ids identifiers to change
*/
public void changeInterfaces(Identifier[] ids, int action) throws SourceException {
Identifier[] old = interfaces;
switch (action) {
case SET:
interfaces = ids;
break;
case REMOVE:
if ((ids.length == 0) || (old.length == 0))
return;
LinkedList list = new LinkedList();
for (int i = 0; i < old.length; i++) {
boolean shouldBeRemoved = false;
for (int j = 0; j < ids.length; j++) {
if (old[i].compareTo(ids[j], false)) { // compare full name
shouldBeRemoved = true;
break;
}
}
if (!shouldBeRemoved)
list.add(old[i]);
}
interfaces = new Identifier[list.size()];
list.toArray(interfaces);
break;
case ADD:
if (ids.length == 0)
return;
interfaces = new Identifier[old.length + ids.length];
System.arraycopy(old, 0, interfaces, 0, old.length);
System.arraycopy(ids, 0, interfaces, old.length, ids.length);
break;
}
try {
regenerateHeader();
firePropertyChange(PROP_INTERFACES, old, interfaces);
}
catch (SourceException e) {
interfaces = old;
throw e;
}
}
/** @return all interfaces which the class implements or interface extends.
*/
public synchronized Identifier[] getInterfaces() {
Identifier[] ret = new Identifier[interfaces.length];
System.arraycopy(interfaces, 0, ret, 0, interfaces.length);
return ret;
}
// ========================= Initializers ==================================
/** Changes set of elements.
* @param elems elements to change
* @param action the action to do (ADD, REMOVE, SET)
* @exception SourceException if the action cannot be handled
*/
public synchronized void changeInitializers(InitializerElement[] elems, int action) throws SourceException {
changeInitializers(elems, action, false);
}
/** Changes set of elements.
* @param elems elements to change
* @param action the action to do (ADD, REMOVE, SET)
* @exception SourceException if the action cannot be handled
*/
private synchronized void changeInitializers(InitializerElement[] elems, int action, boolean skipBoundsCreation) throws SourceException {
initInitializers();
initializers.skipBoundsCreation = skipBoundsCreation;
initializers.change(elems, action);
initializers.skipBoundsCreation = false;
}
public synchronized InitializerElement[] getInitializers() {
initInitializers();
return (InitializerElement[])initializers.toArray();
}
void initInitializers() {
if (initializers == null) {
initializers = new ElementsCollection.Initializer(this);
}
}
// ========================= Fields ==================================
/** Changes set of elements.
* @param elems elements to change
* @exception SourceException if the action cannot be handled
*/
public synchronized void changeFields(FieldElement[] elems, int action) throws SourceException {
changeFields(elems, action, false);
}
/** Changes set of elements.
* @param elems elements to change
* @exception SourceException if the action cannot be handled
*/
public synchronized void changeFields(FieldElement[] elems, int action, boolean skipBoundsCreation) throws SourceException {
initFields();
fields.skipBoundsCreation = skipBoundsCreation;
fields.change(elems, action);
fields.skipBoundsCreation = false;
}
public synchronized FieldElement[] getFields() {
initFields();
return (FieldElement[])fields.toArray();
}
/** Finds a field with given name.
* @param name the name of field to look for
* @return the element or null if field with such name does not exist
*/
public synchronized FieldElement getField(Identifier name) {
initFields();
return (FieldElement)fields.find(name, null);
}
void initFields() {
if (fields == null) {
fields = new ElementsCollection.Field(this);
}
}
// ========================= Methods ==================================
/** Changes set of elements.
* @param elems elements to change
*/
public synchronized void changeMethods(MethodElement[] elems, int action) throws SourceException {
changeMethods(elems, action, false);
}
/** Changes set of elements.
* @param elems elements to change
*/
public synchronized void changeMethods(MethodElement[] elems, int action, boolean skipBoundsCreation) throws SourceException {
initMethods();
methods.skipBoundsCreation = skipBoundsCreation;
methods.change(elems, action);
methods.skipBoundsCreation = skipBoundsCreation;
}
public MethodElement[] getMethods() {
initMethods();
return (MethodElement[])methods.toArray();
}
/** Finds a method with given name and argument types.
* @param name the name of field to look for
* @param arguments for the method
* @return the element or null if such method does not exist
*/
public synchronized MethodElement getMethod(Identifier name, Type[] arguments) {
initMethods();
return (MethodElement)methods.find(name, arguments);
}
void initMethods() {
if (methods == null) {
methods = new ElementsCollection.Method(this);
}
}
// ========================= Constructors ==================================
/** Changes set of elements.
* @param elems elements to change
* @exception SourceException if the action cannot be handled
*/
public synchronized void changeConstructors(ConstructorElement[] elems, int action) throws SourceException {
changeConstructors(elems, action, false);
}
/** Changes set of elements.
* @param elems elements to change
* @exception SourceException if the action cannot be handled
*/
public synchronized void changeConstructors(ConstructorElement[] elems, int action, boolean skipBoundsCreation) throws SourceException {
initConstructors();
constructors.skipBoundsCreation = skipBoundsCreation;
constructors.change(elems, action);
constructors.skipBoundsCreation = false;
}
public synchronized ConstructorElement[] getConstructors() {
initConstructors();
return (ConstructorElement[])constructors.toArray();
}
/** Finds a constructor with argument types.
* @param arguments for the method
* @return the element or null if such method does not exist
*/
public synchronized ConstructorElement getConstructor(Type[] arguments) {
initConstructors();
return (ConstructorElement)constructors.find(null, arguments);
}
void initConstructors() {
if (constructors == null) {
constructors = new ElementsCollection.Constructor(this);
}
}
// ========================= InnerClasses ==================================
/** Changes set of elements.
* @param elems elements to change
*/
public synchronized void changeClasses(ClassElement[] elems, int action) throws SourceException {
changeClasses(elems, action, false);
}
/** Changes set of elements.
* @param elems elements to change
*/
public synchronized void changeClasses(ClassElement[] elems, int action, boolean skipBoundsCreation) throws SourceException {
initClasses();
classes.skipBoundsCreation = skipBoundsCreation;
classes.change(elems, action);
classes.skipBoundsCreation = false;
}
public synchronized ClassElement[] getClasses() {
initClasses();
return (ClassElement[])classes.toArray();
}
/** Finds an inner class with given name.
* @param name the name to look for
* @return the element or null if such class does not exist
*/
public synchronized ClassElement getClass(Identifier name) {
initClasses();
return (ClassElement)classes.find(name, null);
}
void initClasses() {
if (classes == null) {
classes = new ElementsCollection.Class(this);
}
}
// ================ javadoc =========================================
/** @return class documentation.
*/
public JavaDoc.Class getJavaDoc() {
return (JavaDoc.Class) javadoc;
}
// ================ serialization ======================================
public Object readResolve() {
return new ClassElement(this, (SourceElement)null);
}
}
/*
* Log
* 30 Gandalf-post-FCS1.27.1.1 4/18/00 Svatopluk Dedic Fixed firing property
* changes
* 29 Gandalf-post-FCS1.27.1.0 3/27/00 Svatopluk Dedic Disabled computation of
* body hash; no PROP_BODY change event is ever fired.
* 28 src-jtulach1.27 2/14/00 Svatopluk Dedic
* 27 src-jtulach1.26 1/13/00 Petr Hamernik i18n -(2nd round) -
* script bug fixed.
* 26 src-jtulach1.25 1/12/00 Petr Hamernik i18n: perl script used (
* //NOI18N comments added )
* 25 src-jtulach1.24 1/10/00 Petr Hamernik regeneration of
* ClassElements improved (AKA #4536)
* 24 src-jtulach1.23 1/6/00 Petr Hamernik fixed 4321
* 23 src-jtulach1.22 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 22 src-jtulach1.21 10/7/99 Petr Hamernik Java module has its own
* RequestProcessor for source parsing.
* 21 src-jtulach1.20 8/9/99 Ian Formanek Generated Serial Version
* UID
* 20 src-jtulach1.19 7/30/99 Petr Hamernik hopefully fixed bugs
* #2933 and #2943.
* 19 src-jtulach1.18 7/19/99 Petr Hamernik findElement(int)
* implemented
* 18 src-jtulach1.17 6/9/99 Ian Formanek ---- Package Change To
* org.openide ----
* 17 src-jtulach1.16 6/2/99 Petr Hamernik connections of java
* sources
* 16 src-jtulach1.15 5/17/99 Petr Hamernik missing implementation
* added
* 15 src-jtulach1.14 5/14/99 Petr Hamernik getters improved (clone
* the array before return)
* 14 src-jtulach1.13 5/13/99 Petr Hamernik changes in comparing
* Identifier, Type classes
* 13 src-jtulach1.12 5/12/99 Petr Hamernik ide.src.Identifier
* changed
* 12 src-jtulach1.11 5/10/99 Petr Hamernik
* 11 src-jtulach1.10 4/28/99 Petr Hamernik simple synchronization
* using ConnectionCookie
* 10 src-jtulach1.9 4/21/99 Petr Hamernik Java module updated
* 9 src-jtulach1.8 4/2/99 Petr Hamernik
* 8 src-jtulach1.7 4/1/99 Petr Hamernik
* 7 src-jtulach1.6 3/29/99 Petr Hamernik
* 6 src-jtulach1.5 3/29/99 Petr Hamernik
* 5 src-jtulach1.4 3/29/99 Petr Hamernik
* 4 src-jtulach1.3 3/15/99 Petr Hamernik
* 3 src-jtulach1.2 3/10/99 Petr Hamernik
* 2 src-jtulach1.1 2/25/99 Petr Hamernik
* 1 src-jtulach1.0 2/18/99 Petr Hamernik
* $
*/